Skip to content

Automate the Dart/Flutter release for livekit-uniffi - #1323

Merged
hiroshihorie merged 14 commits into
mainfrom
hiroshi/dart-uniffi-release
Sep 8, 2026
Merged

Automate the Dart/Flutter release for livekit-uniffi#1323
hiroshihorie merged 14 commits into
mainfrom
hiroshi/dart-uniffi-release

Conversation

@hiroshihorie

@hiroshihorie hiroshihorie commented Aug 10, 2026

Copy link
Copy Markdown
Member

Before you submit your PR

Make sure the following is true before submitting your PR:

  • I have read the contributing guidelines and validated that this PR will be accepted.
  • I have read and followed the principles regarding breaking changes, testing, and code quality.

PR description

Automates the Dart/Flutter half of the livekit-uniffi release (CLT-2872), the release-side prerequisite for livekit/client-sdk-flutter#1160.

  • Make the generated package publishable. Release builds omit the dev dylib and publish_to, gain LICENSE/README/CHANGELOG and real version constraints, and fail closed for unknown profiles. A dart-clean task keeps stale host dylibs out of release packages, and a shipped analysis_options.yaml silences lints that only fire on generated code.
  • Re-enable the Dart tests disabled since Data tracks UniFFI #1034. The uniffi-dart pin on main already carries the multi-crate codegen fixes from upstream, so this PR no longer changes the pin.
  • Add uniffi-dart-publish.yml, a tag-push workflow (pub.dev only accepts OIDC tokens from tag-push runs). It resolves and validates the tag against the crate version, builds every build-<triple>.zip through the reusable uniffi-cdylib.yml and attaches them to the release, then builds the Dart package from a copy outside the work tree (packages/ is gitignored and pub archives from git's file listing), validates it, and publishes. The publish job depends on the asset job, so there is nothing to poll and a package can never publish without its assets.
  • Harden uniffi-cdylib.yml. It checks out the release tag rather than the dispatch ref, builds the Linux libraries inside manylinux_2_28 containers (glibc 2.28 floor, where ubuntu-latest gives 2.39 and breaks Debian 12 / Ubuntu 22.04 / AL2023), builds aarch64 Linux on a native arm64 runner, and installs cargo-ndk as a prebuilt binary.
  • Pin the Dart SDK to 3.13.1 in both the test and publish workflows so PR CI validates the SDK the publish runs with.

Publishing stays disabled (PUBLISH_ENABLED=false, every run stops at --dry-run) until the first manual publish creates the package on pub.dev and automated publishing is configured. Runbook in the workflow header.

Breaking changes

None. Dev-profile cargo make dart-package behaves as before.

MSRV

No changes.

Testing

  • Dev build: all 5 Dart FFI tests pass, same flow now re-enabled in CI.
  • Release build: dart pub publish --dry-run exits 0 with zero warnings and no dylib in the package.
  • Full release matrix dry run from this branch against the v0.1.9 tag: all 12 cdylib targets plus the Swift and Android jobs passed with dry_run=true (https://github.com/livekit/rust-sdks/actions/runs/34203702564). The CI-built Linux libraries were downloaded and verified: bare .so at the zip root, sha256 sidecars match, highest required symbol version GLIBC_2.28 on both architectures.
  • Consumer: Integrate initial uniffi rust core client-sdk-flutter#1160's uniffi tests pass against the generated package.
  • Not exercisable before merge: the publish workflow's own job wiring, because GitHub only registers dispatchable workflows from the default branch. Plan is to dispatch it from main with dry_run=true before the next livekit-uniffi release. Whether knope's API-created tag fires the push trigger is confirmed only by the first real release. Both cost nothing while publishing is disabled.

Async

No async code added; the crate surface is untouched.

🤖 Generated with Claude Code

…ests

The upstream rev pinned by #1183 predates data tracks, so the bindgen
emitted analysis errors (Bytes vs NativeType) for the multi-crate
surface and the Dart tests have been disabled since #1034. Pin the fork
rev that fixes multi-crate, custom-type and name-collision codegen; the
pin is temporary until upstream merges the fixes or a livekit fork
exists, see the Cargo.toml comment.

Verified locally: cargo make dart-package passes all 5 FFI tests across
the FFI boundary.
Split the dart-package flow by cargo-make profile: dev builds keep the
embedded host dylib and publish_to: none, release builds omit the dylib
(consumers use the hook's download mode) and clear publish_to so pub.dev
accepts the package. The publish_to guard fails closed, an unknown
profile still renders none. A dart-clean task starts every build from
scratch so a release build over a previous dev tree cannot ship the
stale host dylib, which the hook would prefer over download on every
platform.

Also add the metadata pub.dev requires or scores: LICENSE, README,
CHANGELOG, repository/homepage/issue_tracker pubspec fields, real
version constraints for code_assets/hooks (wide ranges, flutter_test
pins meta exactly so carets break older stable channels), and an
analysis_options.yaml so unused-import lints in generated code do not
fail publish validation.

Verified: both profiles build, all 5 FFI tests pass on the dev package,
dart pub publish --dry-run exits 0 on the release package, and a
release build over a dev tree contains no dylib.
@github-actions

Copy link
Copy Markdown
Contributor

Changeset ✓

This PR includes a changeset covering all affected packages:

Package Bump
livekit-uniffi patch

@hiroshihorie hiroshihorie changed the title Automate the Dart/Flutter release for livekit-uniffi (CLT-2872) Automate the Dart/Flutter release for livekit-uniffi Aug 10, 2026
Wire the existing uniffi-cdylib.yml into uniffi-packages.yml so every
livekit-uniffi release carries the build-<triple>.zip archives (and
sha256 sidecars) the Dart build hook downloads at consumer build time.
The assets attach to the already-published release, so the knope assets
marker stays off (a draft-based flow stranded releases before, #1256).

Since this activates the previously dormant uniffi-cdylib.yml on every
release, also bind its tag name through env instead of template
expanding it into the upload script: tag names may contain shell
metacharacters (Actions script injection).

The changeset cuts the release that carries the first assets.
pub.dev's automated publishing only accepts workflows triggered by a
push of a tag matching the configured pattern, so this cannot be a job
in the release-event-triggered uniffi-packages.yml. The workflow builds
the release-profile package (cached, before the asset wait), stages it
outside the work tree (pub archives from git's file listing and the
generated packages/ tree is gitignored), refuses to publish a package
containing a local native library, gates on every cdylib zip having its
sha256 sidecar, and fast-fails on mis-pointed tags or a missing release.
Tag names are env-bound, never template-expanded into scripts, since
the job holds id-token: write for pub.dev token minting.

PUBLISHING IS NOT ENABLED YET (PUBLISH_ENABLED=false): every run stops
at dart pub publish --dry-run until the uniffi-dart pin moves off the
personal fork, a livekit.io publisher admin has done the first manual
publish, and automated publishing is configured on pub.dev. See the
header for the full enablement steps.
@hiroshihorie
hiroshihorie force-pushed the hiroshi/dart-uniffi-release branch from 10fb002 to c343755 Compare August 11, 2026 04:18
@hiroshihorie
hiroshihorie marked this pull request as ready for review August 11, 2026 05:04
@hiroshihorie
hiroshihorie requested a review from ladvoc as a code owner August 11, 2026 05:04
devin-ai-integration[bot]

This comment was marked as resolved.

crate with [UniFFI](https://mozilla.github.io/uniffi-rs/) and
[uniffi-dart](https://github.com/Uniffi-Dart/uniffi-dart).

This is a low-level package. It is consumed by

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice callout! We should make sure this is included in all the READMEs for the package repos (outside the scope of this PR).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, tracking that separately. Out of scope here.

# Flip to "true" once the enablement steps in the header are done. Real
# publishing additionally requires a tag-push trigger; workflow_dispatch
# runs always stop at the dry run (pub.dev rejects their OIDC tokens).
PUBLISH_ENABLED: "false"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Might it make sense to turn this into a workflow input like some of the other workflows do?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An input would be inert here. pub.dev only accepts OIDC tokens from tag-push runs, which have no inputs, and dispatch runs can never publish regardless. Kept the env constant as the gate.

- name: Setup Dart
uses: dart-lang/setup-dart@65eb853c7ba17dde3be364c3d2858773e7144260 # v1.7.2
with:
sdk: stable

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Should this pin to a specific release?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, pinned to 3.13.1 here and in uniffi-dart-test.yml so PR CI validates the same SDK the publish runs with. Bump both together.

run: |
expected=12
missing=0
while true; do

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my fails with swift it's not a good idea to poll, maybe there's another way to trigger it after dylib update? Have you tested that e2e?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair, and no it was not tested e2e. Removed the polling instead: this workflow now calls uniffi-cdylib.yml itself and the publish job needs it, so assets and package come from one tag-push run. Full matrix dry run from this branch against v0.1.9 passed: https://github.com/livekit/rust-sdks/actions/runs/34203702564. The publish workflow itself can only be dispatched once it is on main (GitHub registers workflows from the default branch), so the plan is a dry_run dispatch from main before the 0.2.0 release.

devin-ai-integration[bot]

This comment was marked as resolved.

The multi-crate codegen fixes (Uniffi-Dart/uniffi-dart#150, #154, #156)
are all merged upstream, so the temporary pin to the contributor fork is
no longer needed. Pin the current upstream main rev, as no release tag
has been cut since v0.2.1.

Upstream codegen now emits ignore_for_file hints and drops unused
imports, but it introduces unused locals in async callback trampolines,
so add unused_local_variable to the generated-code lint ignores.
…elease

# Conflicts:
#	livekit-uniffi/Cargo.toml
devin-ai-integration[bot]

This comment was marked as resolved.

The tag name was template-expanded into the shell script. It only comes
from knope or a workflow_dispatch input and the job token is read-only,
but binding it through env matches how the cdylib and Dart publish jobs
already handle it and closes the pattern off.
The Swift and Android reusable workflows already build from
inputs.tag_name. Without it, a manual re-run of the cdylib job for an
older release built whatever ref the dispatch UI selected and uploaded
the result under that release's version with --clobber.
Native builds on ubuntu-latest inherit its glibc 2.39 floor, so the
published .so would fail to load on Debian 12, Ubuntu 22.04 and Amazon
Linux 2023, which is where server-side Dart consumers of the access token
API run. Build inside quay.io/pypa/manylinux_2_28 images instead, the
same approach ffi-builds.yml uses for livekit-ffi. The arm64 target now
runs on a native arm64 runner, which removes the cross-linker setup.
Action metadata formally requires one and actionlint flags every
workflow referencing the action without it. Cosmetic, GitHub runs the
action fine either way.
Each of the three Android matrix jobs compiled cargo-ndk from source on
every run, roughly 3-5 minutes per job on the release critical path.
taiki-e/install-action fetches the prebuilt binary in seconds, matching
how the repo installs cargo-make in the uniffi-deps action.
The publish job used to poll the release for up to 90 minutes waiting for
the cdylib job in uniffi-packages.yml to attach its assets, because
pub.dev only accepts publishes from a tag-push workflow while the assets
came from the release-event workflow. Call the reusable cdylib workflow
from the tag-push workflow instead and make the publish job depend on
it, so assets and package are produced by the same run and there is
nothing to poll. uniffi-packages.yml goes back to Swift and Android only.

The tag/crate version check moves to the resolve-tag job with a sparse
checkout, so a mis-pointed tag still fails before the build matrix
starts. A dry_run input skips the asset upload for exercising the
workflow by hand. Also drops the completed fork-pin enablement step from
the header.
A floating stable channel could pick up a new analyzer lint the week of
a release and fail the publish dry run, which treats any analyzer finding
as a warning. Pin both workflows to the same version so PR CI validates
what the publish will run with, and bump them together.
@hiroshihorie
hiroshihorie merged commit 94988fb into main Sep 8, 2026
23 checks passed
@hiroshihorie
hiroshihorie deleted the hiroshi/dart-uniffi-release branch September 8, 2026 08:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants